/**
 * 记录“当前联系人”
 */
import { getLogger, Service, parseQuery } from '@gnlab/utils'
import { MemSubject, mmap, mdistinct } from '@gnlab/utils/rx'
import type { Services } from '@/services'
import { getExternalRelation } from '@/services/contact-types/externals/api'
import { getFormattedQywxs } from '@/services/qywxs'
import type { ContactWithTransfer } from './definition'
import { makeContactId } from './definition'

const logger = getLogger('services/contacts/current')

export class Current extends Service<Services> {
  private get contacts() {
    return this.services.contacts
  }
  private get transfers() {
    return this.services.transfers
  }
  private get qywxs() {
    return this.services.qywxs.alives
  }

  /**
   * 当前联系人
   */
  private readonly _contact = new MemSubject<ContactWithTransfer | null>(null)
  readonly contact = this._contact.asStream()

  /**
   * 当前联系人的 ID
   *
   * 注意：当联系人的属性更新（但是并没有切换当前联系人）时， current.contact 也会触发一次推送
   *      因此想要明确监听联系人变更事件时，务必监听 current.id 而不是 current.contact
   */
  readonly id = this.contact.pipe(
    mmap(contact => contact?.id ?? null),
    mdistinct(),
  )

  registered() {
    this.initSyncContact()
    void this.handleSpecified()
  }

  /**
   * 同步“当前联系人”状态
   * - 联系人数据更新时，更新 contact 对象
   * - 出现“企业微信被移除”、“转交联系人被收回”等情况时，确认“当前联系人”是否还存在，若不存在，清除“当前联系人”
   */
  private initSyncContact() {
    this.contacts.all.subscribe(() => {
      const contact = this.contact.value
      if (!contact) return

      // newest 为空代表此联系人已经不存在了
      // newest 和 contact 不一样，说明联系人信息变化了
      const newest = this.contacts.getById(contact.id)
      if (!newest)
        this.cancel() // 不确定以后 cancel() 会不会有额外行为，因此这里不直接执行 _contact.next()
      else if (contact !== newest) this._contact.next(newest)
    })
  }

  /**
   * 自动把 URL 中指定的联系人设为当前联系人
   */
  private async handleSpecified() {
    const query = parseQuery(location.href)
    // 传递企微后台可获取到的参数
    const acctid = query['userid'] ?? ''
    const externalUserId = query['external_user_id'] ?? ''
    // 传递 QunTool 内部参数
    let wxid = !acctid ? query['wxid'] ?? '' : ''
    let contactUserId = !acctid ? query['contact_user_id'] ?? '' : ''

    if (!(acctid && externalUserId) && !(wxid && contactUserId)) return // 没有指定当前联系人
    logger.info('指定了当前联系人', { acctid, externalUserId, wxid, contactUserId })

    // 把 accitd 和 externalUserId 转成 wxid 和 contactUserId
    if (acctid) {
      wxid = (await this.acctidToWxid(acctid)) ?? ''
      if (!wxid) return
      contactUserId = (await this.externalUserIdToContactUserId(wxid, externalUserId)) ?? ''
      if (!contactUserId) return
    }
    const contactId = makeContactId(wxid, contactUserId)

    // 每当企微列表更新时，检查是否有目标企微。有，则启动设置当前联系人的流程
    const qywxSubscription = this.qywxs.subscribe(async qywxMap => {
      const qywx = qywxMap.find(qywx => qywx.wxid === wxid)
      if (qywx) {
        qywxSubscription.unsubscribe()
        transferSubscrption.unsubscribe()

        await this.contacts.loadInfo({ id: contactId })
        const contact = this.contacts.getById(contactId)
        if (!contact) return void logger.warn('找不到指定的“当前联系人”')
        logger.info('切换到指定的当前联系人（by qywx）')
        this.contacts.goTop(contactId)
        this.mark(contact)
      }
    })
    // 指定的联系人也可能是转交进来的联系人，每当转交列表更新，如果有目标联系人，也设为当前联系人
    const transferSubscrption = this.transfers.receiveList.subscribe(transfers => {
      const transfer = transfers.get(contactId)
      if (transfer) {
        qywxSubscription.unsubscribe()
        transferSubscrption.unsubscribe()
        logger.info('切换到指定的当前联系人（by transfer）')
        this.contacts.goTop(contactId)
        this.mark(contactId)
      }
    })
  }
  private async acctidToWxid(acctid: string) {
    const qywxsRes = await getFormattedQywxs()
    if (!qywxsRes.success)
      return void logger.warn('[指定联系人] 根据 acctid 获取 wxid 失败', qywxsRes.error)
    const qywx = qywxsRes.data.find(qywx => qywx.acctid === acctid)
    if (!qywx) return void logger.warn('[指定联系人] 未能找到 acctid 对应的企业微信')
    return qywx.wxid
  }
  private async externalUserIdToContactUserId(wxid: string, externalUserId: string) {
    const relationRes = await getExternalRelation({ ownerWxid: wxid, externalUserId })
    if (!relationRes.success)
      return void logger.warn(
        '[指定联系人] 根据 externalUserId 获取 contactUserId 失败',
        relationRes.error,
      )
    if (!relationRes.data.userId)
      return void logger.warn(
        '[指定联系人] 未能通过 externalUserId 找到对应的联系人',
        relationRes.data,
      )
    const contactUserId = relationRes.data.userId
    logger.info('[指定联系人] 取得 contactUserId：' + contactUserId)
    return contactUserId
  }

  /**
   * 将指定联系人设置为当前联系人
   * 可以传入一个 contact 对象，这样如果发现此联系人尚不存在于列表里，会将其写入列表
   */
  mark(contactId: string | ContactWithTransfer) {
    const contact = typeof contactId === 'string' ? this.contacts.getById(contactId) : contactId
    if (!contact) {
      void logger.warn(`找不到 id=${contactId as string} 的联系人，无法将其设为当前联系人`)
      return false
    }

    // 将不在列表中的联系人加入列表
    if (!this.contacts.getById(contact.id)) {
      this.contacts.add(contact)
    }

    // 这里还是从 contacts 里获取 contact 对象来写入，而不是直接写入传进来的 contact 对象
    // 因为前面将 contact 写入列表时可能会进行一些额外处理，导致写入的对象和传进来的不完全一样
    this._contact.next(this.contacts.getById(contact.id))
    return true
  }

  /**
   * 取消选中当前联系人
   */
  cancel() {
    this._contact.next(null)
  }
}
